import socket
import threading
import time

from common.logger_factory import Log
from config import configuration
from service_ex.remote_service import RemoteSmsService


class HeartbeatServer:
    """
    服务器忙于处理连接时，最多可以有5个连接请求等待被处理
    """
    __def_queue_size = 5

    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.clients = {}
        self.lock = threading.Lock()

    def start(self):
        # 创建一个TCP/IP套接字，使用IPv4地址和TCP协议
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((self.host, self.port))
        server_socket.listen(self.__def_queue_size)
        Log.console().info(f"服务器启动在{self.host}:{self.port}")
        while True:
            # 接受一个新的客户端连接
            client_socket, addr = server_socket.accept()
            Log.console().info(f"有新客户端连接：{addr}")
            with self.lock:
                # 记录客户端最近的活动时间
                self.clients[addr] = time.time()
            # 创建新线程处理这个客户端连接
            threading.Thread(target=self.handle_client, args=(client_socket, addr)).start()
            if configuration.ENABLE_MSG_NOTIFY and configuration.CLIENT_ONLINE_NOTIFY:
                RemoteSmsService.sent_heartbeat_msg(f"客户端{addr}已上线！")

    def handle_client(self, client_socket, addr):
        """
        接收并处理来自客户端的数据。每当客户端发送数据（心跳消息）时，服务器会更新该客户端的最近活动时间，并在检
        测到连接断开或出现错误时清理相关资源
        :param client_socket: 与客户端通信的套接字
        :param addr: 客户端的地址
        """
        while True:
            try:
                # 尝试从客户端接收数据
                # 如果未接收到数据（客户端断开连接）或发生套接字错误，则退出循环
                data = client_socket.recv(1024)
                if data:
                    Log.console().debug(f"接收到心跳信号，来自：{addr}")
                    with self.lock:
                        # 更新活动时间
                        self.clients[addr] = time.time()
                else:
                    break
            except socket.error:
                break
        # 客户端已不再活跃，删除该客户端的地址条目
        with self.lock:
            del self.clients[addr]
        client_socket.close()
        Log.console().warning(f"客户端`{addr}`心跳异常！")
        Log.file().error(f"目标客户端心跳异常：{addr}")
        if configuration.ENABLE_MSG_NOTIFY:
            RemoteSmsService.sent_heartbeat_msg(f"客户端{addr}心跳异常，请尽快检查服务的运行状态。")

    def check_heartbeats(self):
        """
        定期检查所有已连接客户端的心跳状态
        """
        while True:
            # 控制检查频率
            time.sleep(configuration.HEARTBEAT_CHECK_FREQUENCY)
            with self.lock:
                for addr, last_heartbeat in list(self.clients.items()):
                    # 检查当前时间与上次心跳时间的差值
                    # 如果差值大于预定义的心跳超时时间，则表示该客户端已超时未发送心跳消息
                    if time.time() - last_heartbeat > configuration.HEARTBEAT_TIMEOUT:
                        Log.console().warning(f"客户端`{addr}`心跳停止！")
                        Log.file().error(f"目标客户端心跳已停止：{addr}")
                        del self.clients[addr]
                        if configuration.ENABLE_MSG_NOTIFY:
                            RemoteSmsService.sent_heartbeat_msg(f"客户端{addr}心跳停止，请及时处理。")
